package com.stu.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;


public class CompassView extends View {

	private Paint markerPaint, textPaint, bearingPaint,
	  skyPaint, groundPaint;
	private RectF boundingBox, innerBoundingBox, bearingRingBox;
	private Path bearingRingPath, skyPath;
	private int textHeight, centerX, centerY, radius, markWidth,
	  pitchX1, pitchX2, zeroPitchX1, zeroPitchX2;
	private float pitch, roll, ringWidth, innerRadius, bearing, centerXPerDegree;
	
	/**
	 * @param h 0 to 359
	 */
	public void setHeading(float h) {
		if( h == 360 )
			h = 0;
		bearing = h;
	}

	/**
	 * @param p -90 to +90
	 */
	public void setPitch(float p) {
		pitch = p;
	}
	
	/**
	 * @param r -180 to +180
	 */
	public void setRoll(float r) {
		roll = r;
	}
	
	public CompassView(Context context) {
		super(context);
		initCompassView(context);
	} 

	public CompassView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initCompassView(context);
	}
	
	public CompassView(Context context, AttributeSet attrs, int defaultStyle) {
		super(context, attrs, defaultStyle);
		initCompassView(context);
	}
	

	private void initCompassView(Context c) {
		roll = 0;
		pitch = 0;
		bearing = 0;

		boundingBox = new RectF();
		bearingRingBox = new RectF();
		innerBoundingBox = new RectF();
		skyPaint = new Paint();
		groundPaint = new Paint();
		skyPath = new Path();
		bearingRingPath = new Path();
		
		textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		textPaint.setColor(Color.WHITE);
		textPaint.setFakeBoldText(true);
		textPaint.setSubpixelText(true);
		textPaint.setTextAlign(Align.CENTER);
		
		textHeight = (int)textPaint.measureText("yY");
		ringWidth = textHeight * 2;
	
		bearingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		bearingPaint.setColor(Color.BLACK);
		bearingPaint.setStrokeWidth(ringWidth);		// strokeWidth is measured from the centerline
		bearingPaint.setStyle(Paint.Style.STROKE);
		
		markerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		markerPaint.setColor(Color.WHITE);
		//markerPaint.setAlpha(200);
		markerPaint.setStrokeWidth(1);
		markerPaint.setStyle(Paint.Style.STROKE);
		
		skyPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		skyPaint.setColor(Color.BLUE);
		skyPaint.setStyle(Paint.Style.FILL);
		
		groundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		groundPaint.setColor(Color.GRAY);
		groundPaint.setStyle(Paint.Style.FILL);
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		
		centerX = w / 2;
		centerY = h / 2;

		radius = Math.min(centerX, centerY);
		markWidth = radius / 3;
		pitchX1 = centerX - markWidth;
		pitchX2 = centerX + markWidth;
		// make zero line 40% of the view width
		zeroPitchX1 = centerX - (int)((0.4 * w / 2)); 
		zeroPitchX2 = zeroPitchX1 + (int)(0.4 * w); 

		boundingBox.set(centerX - radius, centerY - radius, 
		  centerX + radius, centerY + radius);
		innerBoundingBox.set( ringWidth, ringWidth, (centerX + radius) - ringWidth, (centerY + radius) - ringWidth );
		// the bearingRingBox is the centerline of the bearing ring
		float centerLine = ringWidth / 2;
		bearingRingBox.set( centerLine, centerLine, (centerX + radius) - centerLine, (centerY + radius) - centerLine);
		innerRadius = innerBoundingBox.height() / 2;
		centerXPerDegree = (innerBoundingBox.height() / 2) / 45f;
	}

	@Override 
	protected void onDraw(Canvas canvas) {
	
		float tiltDegree = pitch;
		if( tiltDegree > 90 )
			tiltDegree = 90;
		else if( tiltDegree < -90 )
			tiltDegree = -90;
		
		float rollDegree = roll;
		while (rollDegree > 180 || rollDegree < -180) {
			if (rollDegree > 180)
				rollDegree = -180 + (rollDegree - 180);
			if (rollDegree < -180)
				rollDegree = 180 - (rollDegree + 180);
		}
		
		canvas.save();
		canvas.rotate( rollDegree, centerX, centerY );
		canvas.drawOval(innerBoundingBox, groundPaint);

		skyPath.rewind();
		skyPath.addArc(innerBoundingBox, 180 + tiltDegree, 180 - (2 * tiltDegree) );
		canvas.drawPath(skyPath, skyPaint);
		canvas.drawPath(skyPath, markerPaint);

		// Pitch lines
		double h = innerRadius * Math.cos(Math.toRadians(90 - tiltDegree));
		double justTiltY = centerY - h;
	
		for (int i = 90; i >= -90; i -= 10) {
			double ypos = justTiltY + (i * centerXPerDegree);
		
			// Only display the scale within the inner face and below the arrow
			if( (ypos < (innerBoundingBox.top + textHeight)) || 
			  (ypos > innerBoundingBox.bottom - textHeight) )
				continue;
		
			// Draw a line and the tilt angle for each scale increment.
			if( i == 0 ) {
				markerPaint.setStrokeWidth( 2 );
				canvas.drawLine(zeroPitchX1, (float)ypos, zeroPitchX2, (float)ypos, markerPaint);
			} else {
				markerPaint.setStrokeWidth( 1 );
				canvas.drawLine(pitchX1, (float)ypos, pitchX2, (float)ypos, markerPaint);
			}
			int displayPos = (int)(tiltDegree - i);
			String displayString = String.valueOf(displayPos);
			canvas.drawText(displayString, centerX, (int)(ypos) - 2, textPaint);
		}

		canvas.restore();
		
		// Bearing values
		bearingRingPath.rewind();
		bearingRingPath.addOval(bearingRingBox, Direction.CW);
		canvas.drawPath(bearingRingPath, bearingPaint);
		canvas.save();
		int tickCount = (int)bearing / 15;
		int tickValue = tickCount * 15;
		float degrees = tickValue - bearing;
		canvas.rotate(degrees, centerX, centerY);
		for( int i = 0; i < 24; i++ ) {
			// Show a numeric value every 45 degrees
			if (tickValue % 45 == 0) {
				if( tickValue == 360 )
					tickValue = 0;
				String rollString = String.valueOf( tickValue );
				canvas.drawText(rollString, centerX, innerBoundingBox.top, textPaint);
				markerPaint.setStrokeWidth( 4 );
				canvas.drawLine(centerX, (int)boundingBox.top, 
				  centerX, (int)boundingBox.top + textHeight, 
				  markerPaint);
			} else {
				// Otherwise draw a marker line every 15 degrees
				markerPaint.setStrokeWidth( 1 );
				canvas.drawLine(centerX, (int)boundingBox.top, 
				  centerX, (int)boundingBox.top + textHeight, 
				  markerPaint);
			}
			canvas.rotate(15, centerX, centerY);
			tickValue += 15;
		}
		canvas.restore();
	}
} 
